#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import os
import re
import sys
import glob
import hashlib
import mirrors.plugin


class Main:

    def __init__(self, sock):
        self.sock = sock
        self.id = mirrors.plugin.params["id"]
        self.dataDir = mirrors.plugin.params["storage-file"]["data-directory"]

    def run(self):
        selfDir = os.path.dirname(os.path.realpath(__file__))

        idxFile = os.path.join(selfDir, self.id + ".idx")
        print("Reading \"%s\"." % (idxFile))
        fileInfoDict = Util.read_idx_file(idxFile)
        dirSet = set()
        for filename in fileInfoDict:
            dirName = os.path.dirname(filename)
            while dirName != "":
                dirSet.add(dirName)
                dirName = os.path.dirname(dirName)

        # check
        print("Scanning directory \"%s\"." % (self.dataDir))
        srcFileList = glob.glob(os.path.join(self.dataDir, "LaunchBox", "Games", "**"), recursive=True)
        srcFileList = [x for x in srcFileList if not x.endswith("/")]       # remove root directory in the result of glob.glob()
        srcFileList = [(x, x[len(self.dataDir)+1:]) for x in srcFileList]
        processedCount = 0
        progress = -1
        for fullfn, fn in srcFileList:
            print("Processing \"%s\"." % (fn))
            if not os.path.isdir(fullfn):
                if fn not in fileInfoDict:
                    raise Exception("redundant file \"%s\"" % (fn))
                if Util.generate_sha256(fullfn) != fileInfoDict[fn][1]:
                    raise Exception("invalid hash for file \"%s\"" % (fn))
                del fileInfoDict[fn]
                processedCount += 1
                newProgress = processedCount * 100 // len(fileInfoDict)
                if newProgress > progress:                                  # for performance: don't send too many progress change messages
                    self.sock.progress_changed(newProgress)
                    progress = newProgress
            else:
                # MS-DOS games do have empty directories
                # if fn not in dirSet:
                #     raise Exception("redundant directory \"%s\"" % (fn))
                pass
        for fn in fileInfoDict:
            raise Exception("missing file \"%s\"" % (fn))


class Util:

    @staticmethod
    def generate_sha256(file):
        sha256 = hashlib.sha256()
        with open(file, 'rb') as f:
            while True:
                data = f.read(65536)
                if not data:
                    break
                sha256.update(data)
        return sha256.hexdigest()

    @staticmethod
    def read_idx_file(fullfn):
        ret = {}
        with open(fullfn, "r") as f:
            for line in f:
                line = line.rstrip("\n")
                if line == "" or line.startswith("#"):
                    continue
                filehash, filesize, filename = re.split(r"\s+", line, maxsplit=2)                       # hash, size and filepath as remainder of the string
                if filename in ret:
                    raise Exception("duplicate filename \"%s\" in index file \"%s\"" % (filename, fullfn))
                ret[filename] = (filesize, filehash)
        return ret


###############################################################################

if __name__ == "__main__":
    with mirrors.plugin.ApiClient() as sock:
        try:
            Main(sock).run()
            sock.progress_changed(100)
        except Exception:
            sock.error_occured(sys.exc_info())
            raise
